using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace Campus
{
    public abstract class BaseEntity : Sprite
    {
        protected int m_currentHP;
        public int CurrentHP
        {
            get { return m_currentHP; }
        }

        protected int m_maxHP;
        public int MaxHP
        {
            get { return m_maxHP; }
        }

        protected String m_name;
        public String Name
        {
            get { return m_name; }
            set { m_name = value; }
        }

        protected Texture2D m_entitySelectedImage;
        public Texture2D EntitySelectedImage
        {
            get { return m_entitySelectedImage; }
            set { m_entitySelectedImage = value; }
        }

        protected bool m_isSelected;
        public bool IsSelected
        {
            get { return m_isSelected; }
            set { m_isSelected = value; }
        }
        private bool m_isAttacking;
        public bool IsAttacking {
            get { return m_isAttacking; }
            set { m_isAttacking = value; }
        }
        protected bool m_isUsable = true;
        public bool IsUsable {
            get { return m_isUsable; }
            set { m_isUsable = value; }
        }
        private bool m_canAttack;
        public bool CanAttack {
            get { return m_canAttack; }
            protected set { m_canAttack = value; }
        }

        private Queue<Ability> m_abilityQueue;
        public Queue<Ability> AbilityQueue {
            get { return m_abilityQueue; }
        }

        public void SetCurrentAbility(Ability ab) {
            m_abilityQueue.Clear();
            m_abilityQueue.Enqueue(ab);
        }

        public void QueueAbility(Ability ab) {
            m_abilityQueue.Enqueue(ab);
        }

        public int TeamNumber
        {
            get { return m_owner.TeamNumber; }
        }

        protected int m_visionRadius = 200;
        public int VisionRadius
        {
            get { return m_visionRadius; }
            set { m_visionRadius = value; }
        }

        private Player m_owner;
        public Player PlayerOwner {
            get { return m_owner; }
        }

        private Level m_currentLevel;
        public Level CurrentLevel {
            get { return m_currentLevel; }
        }

        /// <summary>
        /// The texture which will be tinted the team color.
        /// </summary>
        public Texture2D TeamColorTexture { get; protected set; }

        protected float m_constructTime = 2000;

        public BaseEntity(Texture2D texture, Texture2D teamColorTexture, Vector2 origin, Vector2 position, int maxHitPoints, Player owner, Level level, 
                Dictionary<String, Ability> abilities, Dictionary<String, AbilitySubMenu> subMenus)
            : base(texture, position) {
            Origin = origin;
            m_maxHP = m_currentHP = maxHitPoints;
            m_owner = owner;
            m_abilities = abilities;
            m_subMenus = subMenus;
            m_abilityQueue = new Queue<Ability>();
            TeamColorTexture = teamColorTexture;
            // Adding to the Level should be handled by the adder (Player, Structure, etc), not the addee (this).
            m_currentLevel = level;
            //m_currentLevel.AddBaseEntity(this);
        }

        public BaseEntity(Animation animation, Vector2 position, int maxHitPoints, Player owner, Level level, 
                Dictionary<String, Ability> abilities, Dictionary<String, AbilitySubMenu> subMenus) : base(animation, position) {
            m_maxHP = m_currentHP = maxHitPoints;
            m_owner = owner;
            m_abilities = abilities;
            m_subMenus = subMenus;
            m_abilityQueue = new Queue<Ability>();
            // Adding to the Level should be handled by the adder (Player, Structure, etc), not the addee (this).
            m_currentLevel = level;
            //m_currentLevel.AddBaseEntity(this);
        }

        public BaseEntity(TextureMapAnimation animation, Vector2 position, int maxHitPoints, Player owner, Level level, 
                Dictionary<String, Ability> abilities, Dictionary<String, AbilitySubMenu> subMenus)
            : base(animation, position) {
            m_maxHP = m_currentHP = maxHitPoints;
            m_owner = owner;
            m_abilities = abilities;
            m_subMenus = subMenus;
            m_abilityQueue = new Queue<Ability>();
            // Adding to the Level should be handled by the adder (Player, Structure, etc), not the addee (this).
            m_currentLevel = level;
            //m_currentLevel.AddBaseEntity(this);
        }

        public void TakeDamage(int damage)
        {
            m_currentHP -= damage;
            if (m_currentHP <= 0)
            {
                Destroy();
            }
        }
        public virtual void Stop() {
        }

        //TODO: add ability / status stuff
        public override void Update(GameTime gameTime, bool checkInput, bool updateBoundingBox)
        {
            while(0 < m_abilityQueue.Count) {
                if (m_abilityQueue.Peek().IsAbilityDone) {
                    m_abilityQueue.Dequeue();
                } else {
                    m_abilityQueue.Peek().Update(gameTime, checkInput, updateBoundingBox);
                    break;
                }
            }
            base.Update(gameTime, checkInput, updateBoundingBox);
        }
        
        public AttackAbility GetAttackAbility() {
            if (CanAttack) {
                if (Abilities.ContainsKey("Attack")) {
                    return (AttackAbility)Abilities["Attack"];
                }
            }
            return null;
        }

        public void FrontQueueAbility(Ability ab) {
            Queue<Ability> temp = new Queue<Ability>();
            while (0 < AbilityQueue.Count) {
                temp.Enqueue(AbilityQueue.Dequeue());
            }

            AbilityQueue.Enqueue(ab);
            while (0 < temp.Count) {
                AbilityQueue.Enqueue(temp.Dequeue());
            }
        }

        abstract protected void Destroy();
        override abstract public String ToString();

        private Dictionary<String, Ability> m_abilities;
        public Dictionary<String, Ability> Abilities {
            get { return m_abilities; }
        }

        private Dictionary<String, AbilitySubMenu> m_subMenus;
        public Dictionary<String, AbilitySubMenu> SubMenus {
            get { return m_subMenus; }
        }

        public abstract Vector2 GetCenter();
    }
}